package magnet.processor.instances.parser;

import com.squareup.javapoet.ClassName;
import magnet.processor.MagnetProcessorEnv;
import magnet.processor.common.KotlinMethodMetadata;
import magnet.processor.common.ValidationException;
import magnet.processor.instances.*;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import java.util.ArrayList;
import java.util.List;

public class InstanceParserForClass extends InstanceParser<TypeElement> {

    public InstanceParserForClass(MagnetProcessorEnv env) {
        super(env, true);
    }

    @Override
    protected List<FactoryType> generateFactories(ParserInstance<TypeElement> instance) throws ValidationException {
        ClassName instanceType = ClassName.get(instance.getElement());
        String instancePackage = instanceType.packageName();

        List<FactoryType> result = new ArrayList<>();

        for (ClassName className : instance.getTypes()) {
            boolean hasSiblingTypes = instance.getTypes().size() > 1;
            GetSiblingTypesMethod getSiblingTypesMethod = null;
            if (hasSiblingTypes) {
                ArrayList<ClassName> types = new ArrayList<>();
                types.addAll(instance.getTypes());
                types.remove(className);
                List<ClassName> siblingTypes = new ArrayList<>();
                for (ClassName type : types) {
                    siblingTypes.add(type);
                    String factoryName = generateFactoryName(true, instanceType, type);
                    siblingTypes.add(ClassName.bestGuess(instancePackage + "." + factoryName));
                }
                getSiblingTypesMethod = new GetSiblingTypesMethod(siblingTypes);
            }
            List<String> selectorAttributes = instance.getSelector();
            GetSelectorMethod getSelectorMethod = null;
            if (selectorAttributes != null) {
                getSelectorMethod = new GetSelectorMethod(selectorAttributes);
            }
            GetLimitMethod getLimitMethod = null;
            if (!instance.getLimitedTo().isEmpty()) {
                getLimitMethod = new GetLimitMethod(instance.getLimitedTo());
            }
            String factoryName = generateFactoryName(hasSiblingTypes, instanceType, className);

            FactoryType factoryType = new FactoryType(
                    instance.getElement(),
                    className,
                    instance.getClassifier(),
                    instance.getScoping(),
                    instance.isDisabled(),
                    ClassName.bestGuess(instancePackage+"."+factoryName),
                    instanceType,
                    instance.getFactory(),
                    instance.getDisposer(),
                    new TypeCreateStatement(instanceType),
                    parseCreateMethod(instance.getElement()),
                    new GetScopingMethod(instance.getScoping()),
                    getLimitMethod,
                    getSelectorMethod,
                    getSiblingTypesMethod
            );

            result.add(factoryType);
        }

        return result;
    }

    private String generateFactoryName(boolean hasSiblingsTypes, ClassName instanceType, ClassName interfaceType) {
        if (hasSiblingsTypes) {
            return getFullName(instanceType) + getFullName(interfaceType) + InstanceParser.FACTORY_SUFFIX;
        } else {
            return getFullName(instanceType) + InstanceParser.FACTORY_SUFFIX;
        }
    }

    private String getFullName(ClassName className) {
        if (className.enclosingClassName() == null) {
            return className.simpleName();
        }
        StringBuilder nameBuilder = new StringBuilder(className.simpleName());
        ClassName typeClassName = className;
        while (typeClassName.enclosingClassName() != null) {
            nameBuilder.insert(0, className.enclosingClassName().simpleName());
            typeClassName = typeClassName.enclosingClassName();
        }
        return nameBuilder.toString();
    }

    private CreateMethod parseCreateMethod(TypeElement element) throws ValidationException {
        List<ExecutableElement> executableElements = ElementFilter.constructorsIn(element.getEnclosedElements());
        List<ExecutableElement> constructors = new ArrayList<>();

        for (ExecutableElement item : executableElements) {
            boolean filter = item.getModifiers().contains(Modifier.PRIVATE) || item.getModifiers().contains(Modifier.PROTECTED);
            if (!filter) {
                constructors.add(item);
            }
        }
        //TODO 此处去掉了对kotlin的支持
        KotlinMethodMetadata methodMeta = null;

        List<MethodParameter> methodParameters = new ArrayList<>();
        ExecutableElement constructor = null;
        if (methodMeta == null) {
            constructor = selectJavaConstructor(constructors, element);
        } else {
            constructor = selectKotlinConstructor(methodMeta);
        }
        if (constructor != null) {
            for (VariableElement parameter : constructor.getParameters()) {
                methodParameters.add(parseMethodParameter(element, parameter, methodMeta));
            }
        }

        return new CreateMethod(methodParameters);
    }

    private ExecutableElement selectJavaConstructor(List<ExecutableElement> constructors,TypeElement element) throws ValidationException {
        if (constructors.size() == 1) {
            return constructors.get(0);
        } else {
            throw new ValidationException(element,
                    "Classes annotated with " + magnet.Instance.class + " must have exactly one" +
                            " public or package-private constructor."
            );
        }
    }

    private ExecutableElement selectKotlinConstructor(KotlinMethodMetadata methodMeta) {
        //TODO 未对kotlin做处理
        return null;
    }
}
